---
title: 给Provider开发者的Riverpod指南
---

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import CodeBlock from "@theme/CodeBlock";
import pubspec from "./getting_started/pubspec";
import dartHelloWorld from "./getting_started/dart_hello_world";
import helloWorld from "./getting_started/hello_world";
import dartPubspec from "./getting_started/dart_pubspec";
import {
  trimSnippet,
  AutoSnippet,
  When,
} from "../../../../src/components/CodeSnippet";

本文是为熟悉 [Provider] 并希望了解Riverpod的开发者所设计的。

## Riverpod 和 [Provider] 之间的关系

Riverpod可以说是Provider的“精神”继承者。而且“Riverpod”这个名字就是“Provider”的变位词。

Riverpod是在寻找解决Provider所面临的技术限制的方案中诞生的。
一开始Riverpod被认为是解决这一问题的Provider的下一个大版本。
但最后并没有这么做，因为这样会是一个相当大的突破性变更，
而Provider是现如今最常用的Flutter package 之一。

当然了，Riverpod和Provider从概念上来说还是差不多的。
两个package都扮演着类似的角色。两者都试图：

- 缓存和小会一些有状态的对象
- 在测试时提供一个模拟这些对象的方法
- 在flutter widget中提供了一个简单的方式来监听这些对象的变化

与此同时，如果Riverpod继续成熟下去，它也可能会成为Provider。

Riverpod修复了Provider的许多基本问题，例如但不限于：

- 显著简化了与“provider”的组合。
  Riverpod提供了简单强大的工具比如 [ref.watch] 和 [ref.listen] ，
  而不是冗长且容易出错的 `ProxyProvider`。
- 允许多个“Provider”公开相同类型的值。
  当你暴露一个int类型或String类型的provider时不需要自定义一个类也能完美地使用。
- 不需要在测试中重新定义provider。
  在Riverpod中默认情况下provider可以直接使用。
- 通过提供一种替代的方法来销毁对象([autoDispose])，减少对“作用域”的过度依赖。
  虽然很强大，但确定一个provider的作用域相当复杂而且很难做对。

还有更多……

Riverpod唯一的缺点就是他需要变更Widget继承的类型才能使用：

- 在Riverpod中你应该扩展 `ConsumerWidget` 类，而不是 `StatelessWidget` 类。
- 在Riverpod中你应该扩展 `ConsumerStatefulWidget` 类，而不是 `StatefulWidget` 类。

但从大局上来看这种不便利的影响还是比较小的。而且这个限制在未来可能被移除。

所以要回答这个问题，你可能会问自己：
**我要使用Provider 还是 Riverpod ？**

你大概率会选择 Riverpod。
Riverpod设计得更好还能急剧简化你的逻辑。

## Provider和Riverpod的差异

### 定义 providers

两个package最主要的区别在于如何定于“provider”。

对 [Provider] 来说，provider是widget，因此被放在widget树中，一般位于 `MultiProvider` 中：

```dart
class Counter extends ChangeNotifier {
 ...
}

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider<Counter>(create: (context) => Counter()),
      ],
      child: MyApp(),
    )
  );
}
```

对于Riverpod来说， provider **不是** widget 而是普通的Dart对象。
类似地，provider定义在widget树之外，并声明为全局final变量。

另外，为了让Riverpod工作，应当在整个应用上方添加一个 `ProviderScope` widget。
下面是Riverpod的provider示例：

```dart
// Providers 现在是顶级变量
final counterProvider = ChangeNotifierProvider<Counter>((ref) => Counter());

void main() {
  runApp(
    // 这个 widget 可以让Riverpod在整个Flutter 项目中使用。
    ProviderScope(
      child: MyApp(),
    ),
  );
}
```

provider的定义只是向上移动了几行。

:::info
由于Riverpod的provider是普通的Dart对象，因此可以同时在Dart和Flutter使用Riverpod。
例如Riverpod也可以编写命令行应用。
:::

### 读取provider: BuildContext

当使用Provider时，读取provider的一种方法是使用widget的 `BuildContext`.

例如，如果provider被定义为：

```dart
Provider<Model>(...);
```

然后使用 [Provider] 读取它：

```dart
class Example extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Model model = context.watch<Model>();

  }
}
```

Riverpod 的情况也一样：

```dart
final modelProvider = Provider<Model>(...);

class Example extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    Model model = ref.watch(modelProvider);

  }
}
```

注意:

- Riverpod的代码段展示了其扩展自 `ConsumerWidget` 类而不是 `StatelessWidget` 类。
  它在build函数中添加了一个额外的参数： `WidgetRef`。

- 不同于 `BuildContext.watch` ，我们在Riverpod中使用来自`ConsumerWidget` 的 `WidgetRef` 来调用 `WidgetRef.watch`。

- Riverpod不依赖泛型类型。相反，它依赖于使用的provider定义创建的变量。

它们的使用关键词很相似。Provider和Riverpod都适用关键字“watch”来描述“当值发生变化时，这个widget应该重新构建”。

:::info
Riverpod使用和Provider相同的术语来读取provider。

- `BuildContext.watch` -> `WidgetRef.watch`
- `BuildContext.read` -> `WidgetRef.read`

`context.watch` 和 `context.read` 的规则也适用于Riverpod：
在 `build` 方法中使用 “watch”。在点击或其他事件中使用 “read”。
:::

### 读取 provider: Consumer

Provider附带了一个名为 `Consumer` (以及 `Consumer2` 之类的变体) 的widget用来读取provider。

`Consumer` 对性能优化很有帮助，它允许更细粒度的widget树重新构建，在状态变更时只更新相关的widget：

因此，如果provider被定义为：

```dart
Provider<Model>(...);
```

Provider允许你通过 `Consumer` 读取provider：

```dart
Consumer<Model>(
  builder: (BuildContext context, Model model, Widget? child) {

  }
)
```

Riverpod也有这样的原则。Riverpod也有一个名为 `Consumer` 的相同目的的widget。

如果我们定义provider为：

```dart
final modelProvider = Provider<Model>(...);
```

然后使用 `Consumer` 我们可以这样：

```dart
Consumer(
  builder: (BuildContext context, WidgetRef ref, Widget? child) {
    Model model = ref.watch(modelProvider);

  }
)
```

请注意这个 `Consumer` 是如何提供给我们 `WidgetRef` 对象的。
这和我们在前面与 `ConsumerWidget` 相关的内容中看到的对象相同。

### 组合 provider: 带有无状态对象的 ProxyProvider

当使用Provider，当组合不同的provider时官方的方法是使用 `ProxyProvider` widget (或者使用比如 `ProxyProvider2` 的变体)。

比如说，我们定义了:

```dart
class UserIdNotifier extends ChangeNotifier {
  String? userId;
}

// ...

ChangeNotifierProvider<UserIdNotifier>(create: (context) => UserIdNotifier()),
```

接下来我们有两个选择： 
我们可能结合 `UserIdNotifier` 来创建一个新的 “无状态(stateless)” 的provider(通常是一个重载了==方法的不可变的值)， 比如：

```dart
ProxyProvider<UserIdNotifier, String>(
  update: (context, userIdNotifier, _) {
    return 'The user ID of the the user is ${userIdNotifier.userId}';
  }
)
```

每当 `UserIdNotifier.userId` 出现变化时provider会自动返回一个新的 `String` 的值。

我们可以在Riverpod中做类似的事，但是语法不同。  
首先，在Riverpod中 `UserIdNotifier` 的定义是：

```dart
class UserIdNotifier extends ChangeNotifier {
  String? userId;
}

// ...

final userIdNotifierProvider = ChangeNotifierProvider<UserIdNotifier>(
  (ref) => UserIdNotifier(),
),
```

然后我们就可以根据 `userId` 生成我们的 `String` ：

```dart
final labelProvider = Provider<String>((ref) {
  UserIdNotifier userIdNotifier = ref.watch(userIdNotifierProvider);
  return 'The user ID of the the user is ${userIdNotifier.userId}';
});
```

注意 `ref.watch(userIdNotifierProvider)` 这一行。


这行代码告诉Riverpod获取 `userIdNotifierProvider` 的内容，
并当值发生变化时， `labelProvider` 也将被重新计算。
因此，当 `userId` 发生变化时，我们的 `labelProvider` 将自动更新 `String`。

`ref.watch` 这一行你应该感觉类似。
这种模式在前面的[如何在widget中读取provider](#读取provider-buildcontext)已经解释过了。
实际上，provider现在可以像widget一样监听其他provider。

### 组合provider: 带有有状态对象的ProxyProvider

在组合provider时，另一个可选的替代的用法是暴露一个有状态的对象，比如 `ChangeNotifier` 实例。

为此我们可以使用 `ChangeNotifierProxyProvider`(如 `ChangeNotifierProxyProvider2` 等变体)。
比如我们可能定义：

```dart
class UserIdNotifier extends ChangeNotifier {
  String? userId;
}

// ...

ChangeNotifierProvider<UserIdNotifier>(create: (context) => UserIdNotifier()),
```

然后，我们可以定义一个基于`UserIdNotifier.userId`的新 `ChangeNotifier`。举个例子：

```dart
class UserNotifier extends ChangeNotifier {
  String? _userId;

  void setUserId(String? userId) {
    if (userId != _userId) {
      print('The user ID changed from $_userId to $userId');
      _userId = userId;
    }
  }
}

// ...

ChangeNotifierProxyProvider<UserIdNotifier, UserNotifier>(
  create: (context) => UserNotifier(),
  update: (context, userIdNotifier, userNotifier) {
    return userNotifier!
      ..setUserId(userIdNotifier.userId);
  },
);
```

这个新的provider创建一个 `UserNotifier` 实例(并且永远不会重新构造)，并在用户ID更改时打印一个字符串。
在Riverpod中，这个功能的实现方式不同于provider。首先，我们的 `UserIdNotifier` 定义为：

```dart
class UserIdNotifier extends ChangeNotifier {
  String? userId;
}

// ...

final userIdNotifierProvider = ChangeNotifierProvider<UserIdNotifier>(
  (ref) => UserIdNotifier(),
),
```

接着，与前面的 `ChangeNotifierProxyProvider` 等价的是：

```dart
class UserNotifier extends ChangeNotifier {}

final userNotfierProvider = ChangeNotifierProvider<UserNotifier>((ref) {
  final userNotifier = UserNotifier();
  ref.listen<UserIdNotifier>(
    userIdNotifierProvider,
    (previous, next) {
      if (previous?.userId != next.userId) {
        print('The user ID changed from ${previous?.userId} to ${next.userId}');
      }
    },
  );

  return userNotifier;
});
```

这段代码的核心是 `ref.listen` 这一行。
`ref.listen` 方法是一个允许你监听其他provider和当provider变更时执行函数的工具。

该函数的 `previous` 和 `next` 参数对应provider 更改前的最后一个值和更改后的新值。

[provider]: https://pub.dev/packages/provider
[ref.watch]: /docs/concepts/reading#using-refwatch-to-observe-a-provider
[ref.listen]: /docs/concepts/reading#using-reflisten-to-react-to-a-provider-change
[autodispose]: /docs/concepts/modifiers/auto_dispose
